export const description =
  'On this page, we’ll dive into the different fundamentals of building web applications with Robyn, including examples and best practices.'


## Building Your First Robyn Application

Robyn is the fastest Python web framework, combining Python's simplicity with Rust's performance. Whether you're building APIs, web services, or full-stack applications, Robyn makes it easy to get started and scale.

### Quick Start

Install Robyn and create your first application in minutes:

```bash
pip install robyn
```

## Understanding Handler Types

Robyn supports both synchronous and asynchronous request handlers, allowing you to choose the best approach for your use case:

- **Synchronous handlers**: Perfect for CPU-bound operations, simple logic, or when you don't need to wait for external resources
- **Asynchronous handlers**: Ideal for I/O-bound operations like database calls, HTTP requests, file operations, or any task that involves waiting


<Row>
<Col>
**Synchronous handlers** are perfect for simple operations, calculations, or when you don't need to wait for external resources:
</Col>
  <Col sticky>

    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    from robyn import Robyn

    app = Robyn(__file__)

    @app.get("/")
    def h(request):
        return "Hello, world"

    app.start(port=8080, host="0.0.0.0") # host is optional, defaults to 127.0.0.1
    ```

    ```python {{title: 'typed'}}
    from robyn import Robyn, Request

    app = Robyn(__file__)

    @app.get("/")
    def h(request: Request):
        return "Hello, world"

    app.start(port=8080, host="0.0.0.0")



    ```
    </CodeGroup>
  </Col>
</Row>
<Row>

  <Col>
    **Asynchronous handlers** are ideal for database operations, HTTP requests, file I/O, or any operation that involves waiting:
  </Col>
  <Col sticky>
    <CodeGroup title="Request" tag="GET" label="/hello_world">

      ```python {{ title: 'untyped' }}
      @app.get("/")
      async def h(request):
          return "Hello, world"

      ```

      ```python {{title: 'typed'}}
      from robyn import Request

      @app.get("/")
      async def h(request: Request) -> str:
          return "Hello, world"

      ```
    </CodeGroup>
  </Col>
</Row>

### Complete Example: User Management API

<Row>
  <Col>
    Here's a comprehensive example that demonstrates both sync and async handlers, proper error handling, and real-world patterns:
  </Col>
  <Col sticky>
    <CodeGroup title="Complete API Example" tag="API" label="/users">
    ```python
    from robyn import Robyn, Request
    import asyncio
    import json
    import time
    from typing import Dict, Any
    
    app = Robyn(__file__)
    
    # In-memory storage for demo (use a real database in production)
    users: Dict[str, Dict[str, Any]] = {
        "1": {"id": "1", "name": "Alice", "email": "alice@example.com", "created_at": "2024-01-01"},
        "2": {"id": "2", "name": "Bob", "email": "bob@example.com", "created_at": "2024-01-02"}
    }
    
    # Const route for health checks (cached in Rust for max performance)
    @app.get("/health", const=True)
    def health_check():
        return {"status": "healthy", "version": "1.0.0"}
    
    # Async handler for database-like operations
    @app.get("/users/:id")
    async def get_user(path_params):
        user_id = path_params["id"]
        
        # Simulate async database lookup
        await asyncio.sleep(0.01)  # Simulate DB query time
        
        if user_id in users:
            return {"success": True, "user": users[user_id]}
        else:
            return {"success": False, "error": "User not found"}, 404
    
    # Get all users with pagination
    @app.get("/users")
    async def list_users(query_params):
        page = int(query_params.get("page", "1"))
        limit = int(query_params.get("limit", "10"))
        
        # Simulate async operation
        await asyncio.sleep(0.01)
        
        user_list = list(users.values())
        start = (page - 1) * limit
        end = start + limit
        
        return {
            "users": user_list[start:end],
            "total": len(user_list),
            "page": page,
            "limit": limit
        }
    
    # Create new user
    @app.post("/users")
    async def create_user(body):
        try:
            data = json.loads(body)
            
            # Validate required fields
            if not data.get("name") or not data.get("email"):
                return {"success": False, "error": "Name and email are required"}, 400
            
            # Generate new ID
            new_id = str(len(users) + 1)
            new_user = {
                "id": new_id,
                "name": data["name"],
                "email": data["email"],
                "created_at": time.strftime("%Y-%m-%d")
            }
            
            # Simulate async database save
            await asyncio.sleep(0.02)
            users[new_id] = new_user
            
            return {"success": True, "user": new_user}, 201
            
        except json.JSONDecodeError:
            return {"success": False, "error": "Invalid JSON"}, 400
    
    # Sync handler for CPU-intensive operations
    @app.post("/calculate")
    def calculate_fibonacci(body):
        try:
            data = json.loads(body)
            n = data.get("number", 10)
            
            if n < 0 or n > 35:  # Prevent excessive computation
                return {"error": "Number must be between 0 and 35"}, 400
            
            # CPU-intensive calculation (runs in thread pool)
            def fib(x):
                if x <= 1:
                    return x
                return fib(x-1) + fib(x-2)
            
            start_time = time.time()
            result = fib(n)
            calc_time = time.time() - start_time
            
            return {
                "input": n,
                "result": result,
                "calculation_time": f"{calc_time:.4f}s"
            }
            
        except json.JSONDecodeError:
            return {"error": "Invalid JSON"}, 400
    
    if __name__ == "__main__":
        app.start(port=8080)
    ```
    </CodeGroup>
  </Col>
</Row>

### Testing Your API

<Row>
  <Col>
    Once your server is running, you can test these endpoints using curl or any HTTP client:
  </Col>
  <Col sticky>
    <CodeGroup title="API Testing" tag="CURL" label="Testing">
    ```bash
    # Test health endpoint
    curl http://localhost:8080/health
    
    # Get a user
    curl http://localhost:8080/users/1
    
    # List users with pagination
    curl "http://localhost:8080/users?page=1&limit=5"
    
    # Create a new user
    curl -X POST http://localhost:8080/users \
      -H "Content-Type: application/json" \
      -d '{"name": "Charlie", "email": "charlie@example.com"}'
    
    # Calculate fibonacci
    curl -X POST http://localhost:8080/calculate \
      -H "Content-Type: application/json" \
      -d '{"number": 20}'
    ```
    </CodeGroup>
  </Col>
</Row>

--- 

## Running Your Application

Robyn applications can be run in several ways, each optimized for different scenarios. Here are the most common approaches:


<Row>
  <Col>
    **Direct execution** - Run your application file directly with various optimization flags:
    
    - `--dev`: Development mode with auto-reload
    - `--fast`: Optimized settings for production
    - `--processes N`: Scale across multiple CPU cores
    - `--workers N`: Multiple workers per process
  </Col>
  <Col sticky>
    <CodeGroup title="Request" tag="GET" label="/hello_world">
      ```bash
      usage: app.py [-h] [--processes PROCESSES] [--workers WORKERS] [--log-level LOG_LEVEL] [--create] [--docs] [--open-browser] [--version]

      Robyn, a fast async web framework with a rust runtime.

      options:
        -h, --help            show this help message and exit
        --processes PROCESSES
                              Choose the number of processes. [Default: 1]
        --workers WORKERS     Choose the number of workers. [Default: 1]
        --dev                 Development mode. It restarts the server based on file changes.
        --log-level LOG_LEVEL
                              Set the log level name
        --create              Create a new project template.
        --docs                Open the Robyn documentation.
        --open-browser        Open the browser on successful start.
        --version             Show the Robyn version.
        --compile-rust-path COMPILE_RUST_PATH
                              Compile rust files in the given path.
        --create-rust-file CREATE_RUST_FILE
                              Create a rust file with the given name.
        --disable-openapi     Disable the OpenAPI documentation.
        --fast                Fast mode. It sets the optimal values for processes, workers and log level. However, you can override them.
      ```
    </CodeGroup>
  </Col>
</Row>

### Common Run Configurations

<Row>
  <Col>
    **Development Mode**: Best for local development with automatic reloading when files change.
  </Col>
  <Col sticky>
    <CodeGroup title="Development" tag="DEV">
    ```bash
    # Basic development mode
    python app.py --dev
    
    # Development with custom port
    python app.py --dev --port 3000
    
    # Development with debug logging
    python app.py --dev --log-level DEBUG
    ```
    </CodeGroup>
  </Col>
</Row>

<Row>
  <Col>
    **Production Mode**: Optimized for performance with multiple processes and workers.
  </Col>
  <Col sticky>
    <CodeGroup title="Production" tag="PROD">
    ```bash
    # Fast mode (automatic optimization)
    python app.py --fast
    
    # Custom scaling configuration
    python app.py --processes 4 --workers 2
    
    # Production with specific log level
    python app.py --fast --log-level INFO
    ```
    </CodeGroup>
  </Col>
</Row>

<Row />

<Row>
  <Col>
    **Module execution** - Use Robyn's CLI module for additional features and consistent behavior across environments:
  </Col>

  <Col sticky>
    <CodeGroup title="Request" tag="GET" label="/hello_world">
      ```bash

      usage: python -m robyn app.py [-h] [--processes PROCESSES] [--workers WORKERS] [--dev] [--log-level LOG_LEVEL] [--create] [--docs] [--open-browser] [--version]


      Robyn, a fast async web framework with a rust runtime.

      options:
        -h, --help            show this help message and exit
        --processes PROCESSES
                              Choose the number of processes. [Default: 1]
        --workers WORKERS     Choose the number of workers. [Default: 1]
        --dev                 Development mode. It restarts the server based on file changes.
        --log-level LOG_LEVEL
                              Set the log level name
        --create              Create a new project template.
        --docs                Open the Robyn documentation.
        --open-browser        Open the browser on successful start.
        --version             Show the Robyn version.
        --compile-rust-path COMPILE_RUST_PATH
                              Compile rust files in the given path.
        --create-rust-file CREATE_RUST_FILE
                              Create a rust file with the given name.
        --disable-openapi     Disable the OpenAPI documentation.
        --fast                Fast mode. It sets the optimal values for processes, workers and log level. However, you can override them.
      ```
    </CodeGroup>
  </Col>
</Row>




---

## Handling Different HTTP Methods

Robyn supports all standard HTTP methods. Here's how to create a complete RESTful API with proper request handling:

<Row>
<Col>
**Complete REST API Example**: Here's a practical example showing all HTTP methods for a blog post API:
</Col>
  <Col sticky>

    <CodeGroup title="REST API" tag="CRUD" label="/posts">

    ```python
    from robyn import Robyn, Request
    import json
    
    app = Robyn(__file__)
    
    # In-memory storage for demo
    posts = {
        "1": {"id": "1", "title": "First Post", "content": "Hello World"},
        "2": {"id": "2", "title": "Second Post", "content": "Learning Robyn"}
    }
    
    # GET - Retrieve all posts
    @app.get("/posts")
    def get_posts(query_params):
        limit = int(query_params.get("limit", "10"))
        posts_list = list(posts.values())[:limit]
        return {"posts": posts_list, "total": len(posts)}
    
    # GET - Retrieve specific post
    @app.get("/posts/:id")
    def get_post(path_params):
        post_id = path_params["id"]
        if post_id in posts:
            return {"post": posts[post_id]}
        return {"error": "Post not found"}, 404
    
    # POST - Create new post
    @app.post("/posts")
    def create_post(body):
        data = json.loads(body)
        post_id = str(len(posts) + 1)
        new_post = {
            "id": post_id,
            "title": data.get("title", ""),
            "content": data.get("content", "")
        }
        posts[post_id] = new_post
        return {"message": "Post created", "post": new_post}, 201
    
    # PUT - Update entire post
    @app.put("/posts/:id")
    def update_post(path_params, body):
        post_id = path_params["id"]
        if post_id not in posts:
            return {"error": "Post not found"}, 404
        
        data = json.loads(body)
        posts[post_id] = {
            "id": post_id,
            "title": data.get("title", ""),
            "content": data.get("content", "")
        }
        return {"message": "Post updated", "post": posts[post_id]}
    
    # PATCH - Partial update
    @app.patch("/posts/:id")
    def patch_post(path_params, body):
        post_id = path_params["id"]
        if post_id not in posts:
            return {"error": "Post not found"}, 404
        
        data = json.loads(body)
        post = posts[post_id]
        
        # Update only provided fields
        if "title" in data:
            post["title"] = data["title"]
        if "content" in data:
            post["content"] = data["content"]
        
        return {"message": "Post updated", "post": post}
    
    # DELETE - Remove post
    @app.delete("/posts/:id")
    def delete_post(path_params):
        post_id = path_params["id"]
        if post_id not in posts:
            return {"error": "Post not found"}, 404
        
        deleted_post = posts.pop(post_id)
        return {"message": "Post deleted", "post": deleted_post}
    ```
    </CodeGroup>
  </Col>
</Row>

---


## Working with JSON and Response Formats

Robyn automatically handles JSON serialization, but also provides flexible response formatting options for different use cases.

<Row>
<Col>
  **Automatic JSON Handling**: Robyn automatically converts Python dictionaries and lists to JSON responses with the correct Content-Type headers.
</Col>
  <Col sticky>

    <CodeGroup title="JSON Responses" tag="JSON" label="/api">

    ```python
    from robyn import Robyn, Request
    from datetime import datetime
    import json
    
    app = Robyn(__file__)

    # Simple JSON response - automatic serialization
    @app.get("/api/status")
    def get_status():
        return {
            "status": "active",
            "timestamp": datetime.now().isoformat(),
            "version": "1.0.0"
        }

    # Complex nested JSON
    @app.get("/api/user/:id")
    def get_user_profile(path_params):
        user_id = path_params["id"]
        return {
            "user": {
                "id": user_id,
                "profile": {
                    "name": "John Doe",
                    "email": "john@example.com",
                    "preferences": {
                        "theme": "dark",
                        "notifications": True
                    }
                },
                "activity": [
                    {"action": "login", "timestamp": "2024-01-15T10:30:00Z"},
                    {"action": "view_post", "timestamp": "2024-01-15T10:35:00Z"}
                ]
            }
        }

    # List/array responses
    @app.get("/api/posts")
    def get_posts():
        return [
            {"id": 1, "title": "First Post", "published": True},
            {"id": 2, "title": "Draft Post", "published": False},
            {"id": 3, "title": "Latest Post", "published": True}
        ]

    # Custom status codes with JSON
    @app.post("/api/posts")
    def create_post(body):
        try:
            data = json.loads(body)
            # Validate required fields
            if not data.get("title"):
                return {"error": "Title is required"}, 400
            
            # Success response
            return {
                "message": "Post created successfully",
                "post": {
                    "id": 123,
                    "title": data["title"],
                    "created_at": datetime.now().isoformat()
                }
            }, 201
        except json.JSONDecodeError:
            return {"error": "Invalid JSON format"}, 400
    ```
    </CodeGroup>
  </Col>
</Row>


## Parameter Injection and Route Handling

Robyn provides powerful parameter injection that automatically extracts and injects request components into your handler functions. This eliminates boilerplate code and makes handlers cleaner and more focused.

<Row>
  <Col>

  **Path Parameters**: Extract dynamic segments from URLs using colon syntax (`:param`)
  
  **Type-Safe Injection**: Use type annotations for automatic parameter injection with IDE support
  

  </Col>
  <Col sticky>

    <CodeGroup title="Path Parameters" tag="GET" label="/users/:id">

    ```python {{ title: 'Type-based injection' }}
    from robyn import Request
    from robyn.types import PathParams

    @app.get("/users/:id/posts/:post_id")
    async def get_user_post(request: Request, path_params: PathParams):
        user_id = path_params["id"]
        post_id = path_params["post_id"]
        
        # Validate parameters
        if not user_id.isdigit():
            return {"error": "Invalid user ID"}, 400
        
        return {
            "user_id": int(user_id),
            "post_id": post_id,
            "url": request.url.path
        }
    ```

    ```python {{ title: 'Name-based injection' }}
    @app.get("/users/:id/posts/:post_id")
    async def get_user_post(request, path_params):
        user_id = path_params["id"]
        post_id = path_params["post_id"]
        
        return {
            "user_id": user_id,
            "post_id": post_id,
            "method": request.method
        }
    ```

    </CodeGroup>


  </Col>
</Row>


<Row>
  <Col>

  **Query Parameters**: Access URL query strings with automatic parsing and type conversion helpers
  

  </Col>
  <Col sticky>

    <CodeGroup title="Query Parameters" tag="GET" label="/search?q=python&page=1">

    ```python {{ title: 'Advanced query handling' }}
    from robyn import Request
    from robyn.robyn import QueryParams

    @app.get("/search")
    async def search_products(request: Request, query_params: QueryParams):
        # Get search parameters with defaults
        query = query_params.get("q", "")
        page = int(query_params.get("page", "1"))
        limit = min(int(query_params.get("limit", "10")), 100)  # Cap at 100
        
        # Boolean parameter
        include_sold = query_params.get("include_sold", "false").lower() == "true"
        
        # Array parameters (?tags=python&tags=web)
        tags = query_params.get_list("tags") or []
        
        # Build response
        return {
            "search_query": query,
            "pagination": {"page": page, "limit": limit},
            "filters": {"include_sold": include_sold, "tags": tags},
            "total_params": len(query_params.to_dict())
        }
    ```

    ```python {{ title: 'Simple query access' }}
    @app.get("/search")
    async def search_simple(query_params):
        # Basic parameter access
        query = query_params.get("q", "")
        page = query_params.get("page", "1")
        
        # Convert to dictionary for processing
        all_params = query_params.to_dict()
        
        return {
            "query": query,
            "page": page,
            "all_params": all_params
        }
    ```

    </CodeGroup>
  </Col>
</Row>

<Row>
  <Col>

  Any request param can be used in the handler function either using type annotations or using the reserved names.
  
  <b>
 Do note that the type annotations will take precedence over the reserved names.
 </b>
  
  Robyn showed Batman example syntaxes of accessing the request params:

  </Col>
  <Col sticky>

    <CodeGroup title="Request" tag="GET" label="/split_request_params">

    ```python
    from robyn.robyn import QueryParams, Headers
    from robyn.types import PathParams, RequestMethod, RequestBody, RequestURL
    
    @app.get("/untyped/query_params")
    def untyped_basic(query_params):
        return query_params.to_dict()
    
    
    @app.get("/typed/query_params")
    def typed_basic(query_data: QueryParams):
        return query_data.to_dict()
    
    
    @app.get("/untyped/path_params/:id")
    def untyped_path_params(query_params: PathParams):
        return query_params  # contains the path params since the type annotations takes precedence over the reserved names
    
    
    @app.post("/typed_untyped/combined")
    def typed_untyped_combined(
            query_params,
            method_data: RequestMethod,
            body_data: RequestBody,
            url: RequestURL,
            headers_item: Headers,
    ):
        return {
            "body": body_data,
            "query_params": query_params.to_dict(),
            "method": method_data,
            "url": url.path,
            "headers": headers_item.get("server"),
        }
    ```

    </CodeGroup>
  </Col>
</Row>

Type Aliases: `Request`, `QueryParams`, `Headers`, `PathParams`, `RequestBody`, `RequestMethod`, `RequestURL`, `FormData`, `RequestFiles`, `RequestIP`, `RequestIdentity`

Reserved Names: `r`, `req`, `request`, `query_params`, `headers`, `path_params`, `body`, `method`, `url`, `ip_addr`, `identity`, `form_data`, `files`

---

As Batman continued to develop his web application with Robyn, he explored more features and implemented them using code samples.

## Customizing Response Formats and Headers


After understanding the dynamic nature of Robyn, Batman, now wanted the ability to customize response formats and headers. Robyn showed him how to do this using dictionaries and Robyn's Response object.

### Using Dictionaries

<Row>
<Col>
Batman learned to customize response formats by returning dictionaries or using Robyn's Response object. He could also set status codes and headers for each response. For example, Batman created a response with a dictionary like this:
</Col>
  <Col sticky>

    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    @app.post("/dictionary")
    async def dictionary(request):
        return {
            "status_code": 200,
            "description": "This is a regular response",
            "type": "text",
            "headers": {"Header": "header_value"},
        }

    ```

    ```python {{title: 'typed'}}
    from robyn import Request

    @app.post("/dictionary")
    async def dictionary(request: Request):
        return {
            "status_code": 200,
            "description": "This is a regular response",
            "type": "text",
            "headers": {"Header": "header_value"},
        }


    ```
    </CodeGroup>
  </Col>
</Row>


### Using the Response object
<Row>
<Col>
To use the Response object, he wrote:
</Col>
  <Col sticky>

    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    from robyn.robyn import Response

    @app.get("/response")
    async def response(request):
        return Response(status_code=200, headers=Headers({}), description="OK")
    ```

    ```python {{title: 'typed'}}
    from robyn.robyn import Response, Request

    @app.get("/response")
    async def response(request: Request):
        return Response(status_code=200, headers=Headers({}), description="OK")

    ```
    </CodeGroup>
  </Col>
</Row>

### Returning a Binary Output

<Row>
<Col>
Batman then wanted to return a binary output from his application. He could do this by setting the type of the response to "binary" and returning a bytes object. For example, he wrote:
</Col>
  <Col sticky>

    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    @app.get("/binary_output_response_sync")
    def binary_output_response_sync(request):
        return Response(
            status_code=200,
            headers={"Content-Type": "application/octet-stream"},
            description="OK",
        )


    @app.get("/binary_output_async")
    async def binary_output_async(request):
        return b"OK"


    @app.get("/binary_output_response_async")
    async def binary_output_response_async(request):
        return Response(
            status_code=200,
            headers={"Content-Type": "application/octet-stream"},
            description="OK",
        )
    ```

    ```python {{title: 'typed'}}
    from robyn import Request, Response

    @app.get("/binary_output_response_sync")
    def binary_output_response_sync(request: Request):
        return Response(
            status_code=200,
            headers={"Content-Type": "application/octet-stream"},
            description="OK",
        )


    @app.get("/binary_output_async")
    async def binary_output_async(request: Request):
        return b"OK"


    @app.get("/binary_output_response_async")
    async def binary_output_response_async(request: Request):
        return Response(
            status_code=200,
            headers={"Content-Type": "application/octet-stream"},
            description="OK",
        )
    ```
    </CodeGroup>
  </Col>
</Row>



---

## Response Headers

Batman, being the world's greatest detective, spotted the `headers` field in the `Response` object. He, naturally wanted to know more about it. Robyn explained that he could use the `headers` field to set response headers. For example, he could set the `Content-Type` header to `application/json` by writing:

### Local Response Headers

<Row>
<Col>
Either, by using the `headers` field in the `Response` object:
</Col>
<Col sticky>


    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    @app.get("/")
    def binary_output_response_sync(request):
        return Response(
            status_code=200,
            headers={"Content-Type": "application/octet-stream"},
            description="OK",
        )
    ```

    ```python {{title: 'typed'}}
    from robyn import Request

    @app.get("/")
    def binary_output_response_sync(request: Request):
        return Response(
            status_code=200,
            headers={"Content-Type": "application/octet-stream"},
            description="OK",
        )

    ```
    </CodeGroup>
  </Col>
</Row>

### Global Response Headers

<Row>

<Col>Or setting the Headers globally *per* router.</Col>

  <Col>
    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    app.add_response_header("content-type", "application/json")
    ```

    ```python {{title: 'typed'}}
    app.add_response_header("content-type", "application/json")
    ```
    </CodeGroup>

  </Col>

<Col>
`add_response_header` appends the header to the list of headers, while `set_response_header` replaces the header if it exists.
</Col>
<Col>

    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    app.set_response_header("content-type", "application/json")
    ```

    ```python {{title: 'typed'}}
    app.set_response_header("content-type", "application/json")
    ```
    </CodeGroup>

  </Col>
  
<Col>
To prevent the headers from getting applied to certain endpoints, you can use the `exclude_response_headers_for` function.
</Col>
<Col>
    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    app.exclude_response_headers_for(["/login", "/signup"])
    ```

    ```python {{title: 'typed'}}
    app.exclude_response_headers_for(["/login", "/signup"])
    ```
    </CodeGroup>

</Col>
</Row>

### Cookies

<Row>
<Col>
Set cookies using the `set_cookies` function.
</Col>
<Col sticky>


    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    @app.get("/")
    def binary_output_response_sync(request):
        response = Response(200, {'type': 'int'}, "desc")
        response.set_cookie(key="fakesession", value="fake-cookie-session-value")
        return response
    ```

    ```python {{title: 'typed'}}
    from robyn import Request

    @app.get("/")
    def binary_output_response_sync(request: Request):
        response = Response(200, {'type': 'int'}, "desc")
        response.set_cookie(key="fakesession", value="fake-cookie-session-value")
        return response
    ```
    </CodeGroup>
  </Col>
</Row>


## Request Headers

Batman, now wanted to know how to read request headers. Robyn explained that he could use the `request.headers` field to read request headers. For example, he could read the `Content-Type` header by writing:

### Local Request Headers

<Row>
<Col>
Either, by using the `headers` field in the `Request` object:
</Col>
<Col sticky>


    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    @app.get("/")
    def binary_output_response_sync(request):
      headers = request.headers

      print("These are the request headers: ", headers)
      existing_header = headers.get("exisiting_header")
      existing_header = headers.get("exisiting_header", "default_value")
      exisiting_header = headers["exisiting_header"] # This syntax is also valid

      headers.set("modified", "modified_value")
      headers["new_header"] = "new_value" # This syntax is also valid

      print("These are the modified request headers: ", headers)
      
      return ""
    ```

    ```python {{title: 'typed'}}
    from robyn import Request

    @app.get("/")
    def binary_output_response_sync(request: Request):
      headers = request.headers

      print("These are the request headers: ", headers)
      existing_header = headers.get("exisiting_header")
      existing_header = headers.get("exisiting_header", "default_value")
      exisiting_header = headers["exisiting_header"] # This syntax is also valid

      headers.set("modified", "modified_value")
      headers["new_header"] = "new_value" # This syntax is also valid

      print("These are the modified request headers: ", headers)
      
      return ""

    ```
    </CodeGroup>
  </Col>
</Row>

<Row>
<Col>
Or by using the global Request Headers:
</Col>
<Col sticky>


    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    app.add_request_header("server", "robyn")
    ```

    ```python {{title: 'typed'}}
    app.add_request_header("server", "robyn")
    ```
    </CodeGroup>
  </Col>
</Row>

<Row>
<Col>
`add_request_header` appends the header to the list of headers, while `set_request_header` replaces the header if it exists.
</Col>
<Col sticky>


    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    app.set_request_header("server", "robyn")
    ```

    ```python {{title: 'typed'}}
    app.set_request_header("server", "robyn")
    ```
    </CodeGroup>
  </Col>
</Row>


---

## Status Codes

<Row>
<Col>
After learning about response formats and headers, Batman learned to set status codes for his responses. 
</Col>
  <Col sticky>

    <CodeGroup title="Request" tag="GET" label="/hello_world">

    ```python {{ title: 'untyped' }}
    from robyn import status_codes

    @app.get("/response")
    async def response(request):
        return Response(status_code=status_codes.HTTP_200_OK, headers=Headers({}), description="OK")
    ```

    ```python {{title: 'typed'}}
    from robyn import status_codes, Request


    @app.get("/response")
    async def response(request: Request):
        return Response(status_code=status_codes.HTTP_200_OK, headers=Headers({}), description="OK")
    ```
    </CodeGroup>
  </Col>
</Row>


---

## What's next?

Great, now Robyn, what is the `Request` Object that you keep talking about?, Batman said. "Next section", said Robyn.

- [The Request Object](/documentation/en/api_reference/request_object)

Batman was also interested to know about the architecture of Robyn. "Next section", said Robyn.

- [Architecture](/documentation/en/architecture)



